<?php
/**
 * Runs in response to a form from view.php. Copies/Pastes an item to the
 * given resourcepage instance, then redirects back to view
 *
 * Currently allows copy/paste of text, weblink, file (including placeholder
 * file?) items
 *
 * Currently does not allow copy/paste of crosslink, section, rssfeed,
 * placeholders (student etexts or tutor etexts), shared section items
 *
 * @copyright &copy; 2006 The Open University
 * @author d.a.woolhead@open.ac.uk
 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
 * @package resourcepage
 *//** */

// Declare transaction wrapper (for handling Exceptions)
$tw = NULL;

// Declare arrays of files copied/to be deleted (for handling Exceptions)
// and items to be deleted because they should not be copied
$arrfiles = array();
$arritems = array();

try {

    // Get resourcepage module instance
    require_once("module_resourcepage.php");
    $mrp=module_resourcepage::$inst;
    $DASHBOARD_COUNTER=DASHBOARD_RESOURCEPAGE_EDIT;
    
    // Check for required Course Module Id
    $id=required_param('id',PARAM_INT);
    $scrollpos=optional_param('scrollpos',0,PARAM_INT);

    // Get various Course Module, Course and Resourcepage info
    $mrp->obtain_view_info($id, null, $cm, $course, $resourcepage);

    // Verify user action allowed for this Course Module
    $mrp->verify_edit_action($cm);

    // Check there is actually a copied item
    if (!empty($_SESSION['copieditem'])) {

        // Pick up item id (Consider storing itemfields on copy - not if copy containers)
        $itemid = $_SESSION['copieditem'];

        // Create DOM Document for storing item info
        $doc = new DOMDocument();

        // Check for container otherwise item
        if (substr($itemid, 0, 1) == 'c') {

            // Add container to xml document
            xml_putcontainer(substr($itemid, 1), $doc, $doc);

        } else {

            // Add item to xml document
            xml_putitem($itemid, $doc, $doc);
        }

        $xmlstr = $doc->saveXML();

        // Wrap all database updates in a transaction
        $tw = new transaction_wrapper();

        // Check document root node and process accordingly
        $rootnode = $doc->firstChild;
        $rootnodename = $rootnode->nodeName;
        if ($rootnodename == 'container') {

            // Extract container from document and add to database
            $objcontainer = xml_getcontainer($rootnode, $resourcepage, $course, $mrp, $tw, $arrfiles, $arritems);

            // Add container as subcontainer of resourcepage container
            $mrp->add_container_subcontainer($resourcepage->containerid, $objcontainer->id);

            // Log copy item action
            add_to_log($course->id, 'resourcepage', 'addcontainer', 'view.php?id='.$cm->id,
            'Added '.$objcontainer->containertype.' #'.$objcontainer->id. ': copy of '.$itemid,$cm->id);

        } elseif ($rootnodename == 'item') {

            // Extract item from document and add to database
            $objitem = xml_getitem($rootnode, $resourcepage, $course, $mrp, $tw, $arrfiles, $arritems);

            // Check if item copied successfully
            if (count($arritems)) {
                throw new Exception('Error copying item '.$itemid.' to resourcepage '.$resourcepage->id);
            }

            // Add item to container
            $mrp->add_container_item($resourcepage->containerid, $objitem->id);

            // Log copy item action
            if (!isset($objitem->itemtype)) {
                $objitem->itemtype = '';
            }
            add_to_log($course->id, 'resourcepage', 'additem', 'view.php?id='.$cm->id,
            'Added '.$objitem->itemtype.' #'.$objitem->id. ': copy of '.$itemid,$cm->id);

        } else {
            throw new Exception('Unrecognised Document root "'.$rootnodename.'"');
        }

        // Delete any copied items that should not have been copied really
        // Note: This way eliminates any sequencing issues
        foreach ($arritems as $item) {
            $mrp->delete_item($item);
        }

        // Commit Transaction
        $tw->commit();

        // Clear copied item
        unset($_SESSION['copieditem']);
    }

    // Return to resourcepage view
    redirect('view.php?id='.$id.($scrollpos ? '&scrollpos='.$scrollpos : ''));

} catch (Exception $e) {

    // Rollback any transaction
    if (!is_null($tw)) {
        $tw->rollback();
    }

    // Delete any copied files
    foreach ($arrfiles as $file) {
        @unlink($file);
    }

    // Log and display message for any error
    error_log('Paste Item Exception: '.$e->getMessage());
    error(get_string('error_copying_item','resourcepage'),
                     'view.php?id='.$id.($scrollpos ? '&scrollpos='.$scrollpos : ''));

}

/*
 * Add container to node of xml document
 * @param int $containerid Container Id
 * @param obj $resourcepagenode Node to add container under reference
 * @param obj $doc XML document reference
 * @return None - throws Exception on Error
 */
function xml_putcontainer($containerid, &$resourcepagenode, &$doc) {

    // Get container record
    $objcontainer = get_record('resourcepage_containers', 'id', $containerid);

    // Add container to document
    if (!empty($objcontainer)) {
        $elcontainer = $doc->createElement('container');
        $containernode = $resourcepagenode->appendChild($elcontainer);
        foreach ($objcontainer as $field => $value) {
            if (!is_null($value)) {
                if ((strpos($value, '&') !== false) || (strpos($value, '<') !== false) ) {
                    $el = $doc->createElement($field);
                    $cdata = $doc->createCDATASection($value);
                    $node = $el->appendChild($cdata);
                } else {
                    $el = $doc->createElement($field, $value);
                }
                $node = $containernode->appendChild($el);
            }
        }

        // Get any container items
        // Note: Cannot use get_records as first col is not unique
        $rs = get_recordset('resourcepage_containeritems', 'containerid', $containerid);
        if ($rs && $rs->RecordCount() > 0) {
            $elcontaineritems = $doc->createElement('containeritems');
            $containeritemsnode = $containernode->appendChild($elcontaineritems);
            while (!$rs->EOF) {
                $elcontaineritem = $doc->createElement('containeritem');
                $containeritemnode = $containeritemsnode->appendChild($elcontaineritem);
                    $objcontaineritem = $rs->FetchObj();
                    foreach ($objcontaineritem as $field => $value) {
                    if (!is_null($value)) {
                        $el = $doc->createElement($field, $value);
                        $node = $containeritemnode->appendChild($el);

                        // Check for itemid field for adding actual item to document
                        if ($field == 'itemid' && !empty($value)) {
                            xml_putitem($value, $containeritemnode, $doc);
                        } elseif ($field == 'subcontainerid' && !empty($value)) {
                            xml_putcontainer($value, $containeritemnode, $doc);
                        }
                    }
                }
                $rs->MoveNext();
            }
        }

        // Get any container rssfeeds
        // Note: Can use get_records as first col is unique, but used to get_recordset now!
        $rs = get_recordset('resourcepage_x_rssfeed', 'containerid', $containerid);
        if ($rs && $rs->RecordCount() > 0) {
            $elrssfeeds = $doc->createElement('rssfeeds');
            $rssfeedsnode = $containernode->appendChild($elrssfeeds);
            while (!$rs->EOF) {
                $elrssfeed = $doc->createElement('rssfeed');
                $rssfeednode = $rssfeedsnode->appendChild($elrssfeed);
                    $objfile = $rs->FetchObj();
                    foreach ($objfile as $field => $value) {
                    $el = $doc->createElement($field, $value);
                    $node = $rssfeednode->appendChild($el);
                }
                $rs->MoveNext();
            }
        }
    }
}

/*
 * Add item to node of xml document
 * @param int $itemid Item Id
 * @param obj $containernode Node to add item under reference
 * @param obj $doc XML document reference
 * @return None - throws Exception on Error
 */
function xml_putitem($itemid, &$containernode, &$doc) {

    // Get item record
    $objitem = get_record('resourcepage_items', 'id', $itemid);

    // Add item to document
    if (!empty($objitem)) {
        $elitem = $doc->createElement('item');
        $itemnode = $containernode->appendChild($elitem);
        foreach ($objitem as $field => $value) {
            if (!is_null($value)) {
                if ((strpos($value, '&') !== false) || (strpos($value, '<') !== false) ) {
                    $el = $doc->createElement($field);
                    $cdata = $doc->createCDATASection($value);
                    $node = $el->appendChild($cdata);
                } else {
                    $el = $doc->createElement($field, $value);
                }
                $node = $itemnode->appendChild($el);

            }
        }

        // Get any item flags (Currently only text or weblink ... items)
        // Note: Cannot use get_records as first col is not unique
        $rs = get_recordset('resourcepage_itemflags', 'itemid', $itemid);
        if ($rs && $rs->RecordCount() > 0) {
            $elflags = $doc->createElement('flags');
            $flagsnode = $itemnode->appendChild($elflags);
            while (!$rs->EOF) {
                $elflag = $doc->createElement('flag');
                $flagnode = $flagsnode->appendChild($elflag);
                    $objflag = $rs->FetchObj();
                    foreach ($objflag as $field => $value) {
                    $el = $doc->createElement($field, $value);
                    $node = $flagnode->appendChild($el);
                }
                $rs->MoveNext();
            }
        }

        // Get any item files (Currently only file ... items)
        // Note: Cannot use get_records as first col is not unique
        $rs = get_recordset('resourcepage_files', 'itemid', $itemid);
        if ($rs && $rs->RecordCount() > 0) {
            $elfiles = $doc->createElement('files');
            $filesnode = $itemnode->appendChild($elfiles);
            while (!$rs->EOF) {
                $elfile = $doc->createElement('file');
                $filenode = $filesnode->appendChild($elfile);
                    $objfile = $rs->FetchObj();
                    foreach ($objfile as $field => $value) {
                    $el = $doc->createElement($field, $value);
                    $node = $filenode->appendChild($el);
                }
                $rs->MoveNext();
            }
        }
    }
}

/*
 * Get item(s) from node of xml document and add to database
 * @param obj $containernode Node to get item(s) from
 * @param obj $resourcepage Resourcepage object
 * @param obj $course Course object
 * @param obj $mrp Resourcepage Module object
 * @param obj $tw Transaction wrapper object reference
 * @param array $arrfiles Files Added array reference
 * @param array $arritems Items Added that should not be copied array reference
 * @return obj $objcontainer New container object - throws Exception on Error
 */
function xml_getcontainer($containernode, $resourcepage, $course, $mrp, &$tw, &$arrfiles, &$arritems) {

    // Reconstruct the resourcepage container object and table
    $objcontainer = new stdClass;
    $containeritemsnode = NULL;
    $rssfeedsnode = NULL;
    foreach ($containernode->childNodes as $node) {
        if ($node->nodeName == 'containeritems') {
            // Store containeritems node for later use
            $containeritemsnode = $node;
        } elseif ($node->nodeName == 'rssfeeds') {
            // Store rssfeeds node for later use
            $rssfeedsnode = $node;
        } else {
            // Although this works
            //$objitem->{$node->nodeName} = $node->nodeValue;
            // This is actually the correct way I believe
            // Note: $node->firstChild->nodeName=#text
            $objcontainer->{$node->nodeName} = addslashes($node->firstChild->nodeValue);
        }
    }

    // Reset item id for insert
    unset($objcontainer->id);

    // Set required resourcepageid for insert
    $objcontainer->resourcepageid = $resourcepage->id;

    // Insert resourcepage_containers table record
    $objcontainer->id = insert_record('resourcepage_containers', $objcontainer, true);

    // Insert containeritems
    if ($containeritemsnode) {
        foreach ($containeritemsnode->childNodes as $containeritemnode) {
            $objcontaineritem = new stdClass;
            $objitem = new stdClass;
            $objsubcontainer = new stdClass;
            foreach ($containeritemnode->childNodes as $node) {
                if ($node->nodeName == 'item') {
                    $objitem = xml_getitem($node, $resourcepage, $course, $mrp, $tw, $arrfiles, $arritems);
                    $objcontaineritem->itemid = $objitem->id;
                } elseif ($node->nodeName == 'container') {
                    $objsubcontainer =  xml_getcontainer($node, $resourcepage, $course, $mrp, $tw, $arrfiles, $arritems);
                    $objcontaineritem->subcontainerid = $objsubcontainer->id;
                } elseif ($node->nodeName != 'itemid' && $node->nodeName != 'subcontainerid') {
                    $objcontaineritem->{$node->nodeName} = $node->firstChild->nodeValue;
                }
            }

            // Set container item containerid and itemid field
            // Note: No subcontainerid's at the moment
            $objcontaineritem->containerid = $objcontainer->id;

            // Note: cannot use insert_record as no primary key col in table
            //if (!insert_record('resourcepage_containeritems', $objcontaineritem, false)) {
            //    throw new Exception('Unable to update resourcepage_containeritems');
            //}
            $fields = '';
            $values = '';
            foreach ($objcontaineritem as $field => $value) {
                $fields .= $field.', ';
                $values .= sql_char($value).', ';
            }
            $mrp->do_sql('INSERT INTO prefix_resourcepage_containeritems' .
                         ' ( '.substr($fields, 0, -2).' ) VALUES ( '.substr($values, 0, -2).' )');
        }
    }

    // Insert rssfeeds
    if ($rssfeedsnode) {
        foreach ($rssfeedsnode->childNodes as $rssfeednode) {
            $objrssfeed = new stdClass;
            foreach ($rssfeednode->childNodes as $node) {
                $objrssfeed->{$node->nodeName} = $node->firstChild->nodeValue;
            }

            // Set rssfeed containerid field
            $objrssfeed->containerid = $objcontainer->id;

            // Note: cannot use insert_record as no primary key col in table
            //if (!insert_record('resourcepage_containeritems', $objcontaineritem, false)) {
            //    throw new Exception('Unable to update resourcepage_containeritems');
            //}
            $fields = '';
            $values = '';
            foreach ($objrssfeed as $field => $value) {
                $fields .= $field.', ';
                $values .= sql_char($value).', ';
            }
            $mrp->do_sql('INSERT INTO prefix_resourcepage_x_rssfeed' .
                         ' ( '.substr($fields, 0, -2).' ) VALUES ( '.substr($values, 0, -2).' )');
        }
    }

    // Return container object
    return $objcontainer;
}

/*
 * Get item node elements of xml document and add to database
 * @param obj $itemnode Item node object
 * @param obj $course Course object
 * @param obj $mrp Resourcepage Module object
 * @param obj $tw Transaction wrapper object reference
 * @param array $arrfiles Files Added array reference
 * @param array $arritems Items Added that should not be copied array reference
 * @return obj $objitem New item object - throws Exception on Error
 */
function xml_getitem($itemnode, $resourcepage, $course, $mrp, &$tw, &$arrfiles, &$arritems) {

    // Reconstruct the resourcepage item object and table
    $objitem = new stdClass;
    $flagsnode = NULL;
    $filesnode = NULL;
    foreach ($itemnode->childNodes as $node) {
        if ($node->nodeName == 'flags') {
            // Store flags node for later use
            $flagsnode = $node;
        } elseif ($node->nodeName == 'files') {
            // Store files node for later use
            $filesnode = $node;
        } else {
            // Although this works
            //$objitem->{$node->nodeName} = $node->nodeValue;
            // This is actually the correct way I believe
            // Note: $node->firstChild->nodeName=#text
            $objitem->{$node->nodeName} = addslashes($node->firstChild->nodeValue);
        }
    }

    // Reset item id for insert
    unset($objitem->id);

    // Insert resourcepage_item table record
    $objitem->id = insert_record('resourcepage_items', $objitem, true);

    // Add to list to be deleted later if necessary
    // Avoiding any sequencing issues
    // Checking if crosslink still valid (i.e. still references another resource page on this course)
    if (!empty($objitem->itemtype) && ($objitem->itemtype == 'placeholder' || ($objitem->itemtype == 'crosslink') &&
        !array_key_exists($objitem->crosslink, $mrp->get_other_resourcepages($course->id, $resourcepage->id)))) {
        $arritems[] = $objitem->id;
    }

    // Insert any flags if necessary
    if ($flagsnode) {
        foreach ($flagsnode->childNodes as $flagnode) {
            $objflag = new stdClass;
            foreach ($flagnode->childNodes as $node) {
                $objflag->{$node->nodeName} = $node->firstChild->nodeValue;
            }
            $objflag->itemid = $objitem->id;

            // Note: cannot use insert_record as no primary key col in table
            //if (!insert_record('resourcepage_itemflags', $objflag, false)) {
            //    throw new Exception('Unable to update resourcepage_itemflags');
            //}
            $fields = '';
            $values = '';
            foreach ($objflag as $field => $value) {
                $fields .= $field.', ';
                $values .= sql_char($value).', ';
            }
            $mrp->do_sql('INSERT INTO prefix_resourcepage_itemflags' .
                            ' ( '.substr($fields, 0, -2).' ) VALUES ( '.substr($values, 0, -2).' )');
        }
    }

    // Copy/Insert any files if necessary
    if ($filesnode) {

        // Check/Create directory for storing files
        $dirdest = $mrp->get_save_folder($course);

        // Pick up file directory prefix
        global $CFG;
        $dataroot = $CFG->dataroot;

        foreach ($filesnode->childNodes as $filenode) {
            $objfile = new stdClass;
            foreach ($filenode->childNodes as $node) {
                $objfile->{$node->nodeName} = $node->firstChild->nodeValue;
            }

            // Set new copied/pasted file object item id
            $objfile->itemid = $objitem->id;

            // Check destination filename does not exist, renaming if necessary
            // TODO Consider based on item if it does exist rather than copying file
            $filedest = basename($objfile->filepath);

            if (file_exists($dirdest.'/'.$filedest)) {
                if (($i = strrpos($filedest, '.'))) {
                    $file = ltrim(substr($filedest, 0, $i));
                    $ext = substr($filedest, $i);
                } elseif ($i === false) {
                    $file = $filedest;
                    $ext = '';
                } else {
                    $file = '';
                    $ext = $filedest;
                }

                $i = 1;
                while (file_exists($dirdest.'/'.$file.'_'.$i.$ext) && $i++ < 1000) ;

                // Bail out if more than 1000 identical files!!!
                if ($i > 1000) {
                    throw new Exception('more than 1000 copies of '.$filedest);
                }
                $filedest = $file.'_'.$i.$ext;
            }

            // Copy file
            if (!copy($dataroot.'/'.$objfile->filepath, $dirdest.'/'.$filedest)) {
               throw new Exception('Unable to copy file '.$objfile->filepath);
            }

            // Store copied filename in case of later Exception
            $arrfiles[] = $dirdest.'/'.$filedest;

            // Validate copied file length
            if ($objfile->size != filesize($dirdest.'/'.$filedest)) {
               throw new Exception('Copy of file '.$objfile->filepath.' size error');
            }

            // Set new copied/pasted file object filepath (ensuring no leading '/')
            $objfile->filepath = substr($dirdest.'/'.$filedest, strlen($dataroot) + 1);

            // Note: cannot use insert_record as no primary key col in table
            //if (!insert_record('resourcepage_files', $objfile, false)) {
            //    throw new Exception('Unable to update resourcepage_itemflags');
            //}
            $fields = '';
            $values = '';
            foreach ($objfile as $field => $value) {
                $fields .= $field.', ';
                $values .= sql_char($value).', ';
            }
            $mrp->do_sql('INSERT INTO prefix_resourcepage_files' .
                         ' ( '.substr($fields, 0, -2).' ) VALUES ( '.substr($values, 0, -2).' )');
        }
    }

    // Return item object
    return $objitem;
}
?>